# Copyright 2025 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# LINT.IfChange(forked_exports)
"""
All build variables we create for various `CppCompileAction`s
"""

load(":common/cc/cc_helper_internal.bzl", "extensions", _PRIVATE_STARLARKIFICATION_ALLOWLIST = "PRIVATE_STARLARKIFICATION_ALLOWLIST")

_cc_internal = _builtins.internal.cc_internal
_cc_common_internal = _builtins.internal.cc_common

# deliberately short name for less clutter while using in this file, we can have
# a different symbol with a more descriptive name if we ever export this
_VARS = struct(
    # Variable for the collection of framework include paths.
    FRAMEWORK_PATHS = "framework_include_paths",
    # Variable for the path to the source file being compiled.
    SOURCE_FILE = "source_file",
    # Variable for all flags coming from copt rule attribute, and from --copt,
    # --cxxopt, or --conlyopt options.
    USER_COMPILE_FLAGS = "user_compile_flags",
    # Variable for the path to the compilation output file.
    OUTPUT_FILE = "output_file",
    # Variable for the dependency file path
    DEPENDENCY_FILE = "dependency_file",
    # Variable for the serialized diagnostics file path
    SERIALIZED_DIAGNOSTICS_FILE = "serialized_diagnostics_file",
    # Variable for the module file name.
    MODULE_NAME = "module_name",
    # Variable for the collection of include paths.
    INCLUDE_PATHS = "include_paths",
    # Variable for the collection of quote include paths.
    QUOTE_INCLUDE_PATHS = "quote_include_paths",
    # Variable for the collection of system include paths.
    SYSTEM_INCLUDE_PATHS = "system_include_paths",
    # Variable for the collection of external include paths.
    EXTERNAL_INCLUDE_PATHS = "external_include_paths",
    # Variable for the module map file name.
    MODULE_MAP_FILE = "module_map_file",
    # Variable for the dependent module map file name.
    DEPENDENT_MODULE_MAP_FILES = "dependent_module_map_files",
    # Variable for the collection of module files.
    MODULE_FILES = "module_files",
    # Variable for the collection of macros defined for preprocessor. */
    PREPROCESSOR_DEFINES = "preprocessor_defines",
    # Variable for the gcov coverage file path.
    GCOV_GCNO_FILE = "gcov_gcno_file",
    # Variable for the minimized LTO indexing bitcode file, used by the LTO
    # indexing action. This file was generated by CppCompile actions. For
    # efficiency, it contains minimal information that is required by the LTO
    # indexing action.
    LTO_INDEXING_BITCODE_FILE = "lto_indexing_bitcode_file",
    # Variable for the LTO index file, used by the LTO backend action. This file
    # was generated by the LTO indexing action.
    THINLTO_INDEX = "thinlto_index",
    # Variable for the bitcode file that is input to LTO backend.
    THINLTO_INPUT_BITCODE_FILE = "thinlto_input_bitcode_file",
    # Variable for the object file that is output by LTO backend.
    THINLTO_OUTPUT_OBJECT_FILE = "thinlto_output_object_file",
    # Variable marking fission is used.
    IS_USING_FISSION = "is_using_fission",
    # Variable for the per object debug info file.
    PER_OBJECT_DEBUG_INFO_FILE = "per_object_debug_info_file",
    # Variable present when the output is compiled as position independent.
    PIC = "pic",
    # Variable marking memprof profile is being used
    IS_USING_MEMPROF = "is_using_memprof",
    # Variable for includes that compiler needs to include into sources.
    INCLUDES = "includes",
)

# buildifier: disable=name-conventions
_UnboundValueProviderDoNotUse = provider("This provider is used as an unique symbol to distinguish between bound and unbound Starlark values, to avoid using kwargs.", fields = [])
_UNBOUND = _UnboundValueProviderDoNotUse()

# IMPORTANT: This function is public API exposed on cc_common module!
def create_compile_variables(
        *,
        cc_toolchain,
        feature_configuration,
        source_file = None,
        output_file = None,
        user_compile_flags = None,
        includes = None,
        include_directories = None,
        quote_include_directories = None,
        system_include_directories = None,
        framework_include_directories = None,
        preprocessor_defines = None,
        thinlto_index = None,
        thinlto_input_bitcode_file = None,
        thinlto_output_object_file = None,
        use_pic = False,
        # TODO(b/65151735): Remove once we migrate crosstools to features
        add_legacy_cxx_options = False,  # buildifier: disable=unused-variable
        variables_extension = {},
        strip_opts = _UNBOUND,
        input_file = _UNBOUND):
    """Returns variables used for compilation actions.

    Args:
        cc_toolchain: cc_toolchain for which we are creating build variables.
        feature_configuration: Feature configuration to be queried.
        source_file: Optional source file path for the compilation. Please prefer passing source_file
            here over appending it to the end of the command line generated from
            cc_common.get_memory_inefficient_command_line, as then it's in the power of
            the toolchain author to properly specify and position compiler flags.
        output_file: Optional output file path of the compilation. Please prefer passing output_file
            here over appending it to the end of the command line generated from
            cc_common.get_memory_inefficient_command_line, as then it's in the power of
            the toolchain author to properly specify and position compiler flags.
        includes: paths to headers that should be included using -include
        user_compile_flags: List of additional compilation flags (copts).
        include_directories: Depset of include directories.
        quote_include_directories: Depset of quote include directories.
        system_include_directories: Depset of system include directories.
        framework_include_directories: Depset of framework include directories.
        preprocessor_defines: Depset of preprocessor defines.
        thinlto_index: LTO index file path.
        thinlto_input_bitcode_file: Bitcode file that is input to LTO backend.
        thinlto_output_object_file: Object file that is output by LTO backend.
        use_pic: When true the compilation will generate position independent code.
        add_legacy_cxx_options: Unused.
        variables_extension: A dictionary of additional variables used by compile actions.
        strip_opts: (Private API)
        input_file: (Private API)

    Returns:
      (CcToolchainVariables) common compile build variables
    """
    if strip_opts != _UNBOUND or input_file != _UNBOUND:
        _cc_internal.check_private_api(allowlist = _PRIVATE_STARLARKIFICATION_ALLOWLIST)
    if strip_opts == _UNBOUND:
        strip_opts = []
    if input_file == _UNBOUND:
        input_file = None
    if (use_pic and not feature_configuration.is_enabled("pic") and not feature_configuration.is_enabled("supports_pic")):
        fail("PIC compilation is requested but the toolchain does not support it " +
             "(feature named 'supports_pic' is not enabled)")

    cpp_configuration = cc_toolchain._cpp_configuration
    fdo_context = cc_toolchain._fdo_context

    common_vars = _setup_common_compile_build_variables_internal(
        feature_configuration = feature_configuration,
        is_using_memprof = getattr(fdo_context, "memprof_profile_artifact", None) != None,
        fdo_build_stamp = _get_fdo_build_stamp(cpp_configuration, fdo_context, feature_configuration),
        variables_extension = variables_extension,
        includes = includes or [],
        include_dirs = include_directories or depset(),
        quote_include_dirs = quote_include_directories or depset(),
        system_include_dirs = system_include_directories or depset(),
        framework_include_dirs = framework_include_directories or depset(),
        defines = preprocessor_defines or depset(),
    )

    additional_build_variables = {}
    additional_build_variables["stripopts"] = strip_opts
    if input_file:
        additional_build_variables["input_file"] = input_file

    variables = get_specific_compile_build_variables(
        feature_configuration,
        use_pic = use_pic,
        source_file = source_file,
        output_file = output_file,
        thinlto_index = thinlto_index,
        thinlto_bitcode_file = thinlto_input_bitcode_file,
        thinlto_output_object_file = thinlto_output_object_file,
        additional_build_variables = additional_build_variables,
        user_compile_flags = user_compile_flags or [],
    )
    return _cc_internal.combine_cc_toolchain_variables(cc_toolchain._build_variables, common_vars, variables)

# buildifier: disable=function-docstring
def setup_common_compile_build_variables(
        *,
        cc_compilation_context,
        cc_toolchain,
        cpp_configuration,
        fdo_context,
        feature_configuration,
        variables_extension):
    common_vars = _setup_common_compile_build_variables_internal(
        feature_configuration = feature_configuration,
        is_using_memprof = getattr(fdo_context, "memprof_profile_artifact", None) != None,
        fdo_build_stamp = _get_fdo_build_stamp(cpp_configuration, fdo_context, feature_configuration),
        variables_extension = variables_extension,
        include_dirs = cc_compilation_context.includes,
        quote_include_dirs = cc_compilation_context.quote_includes,
        system_include_dirs = cc_compilation_context.system_includes,
        framework_include_dirs = cc_compilation_context.framework_includes,
        defines = cc_compilation_context.defines,
        local_defines = cc_compilation_context.local_defines,
        external_include_dirs = cc_compilation_context.external_includes,
    )
    return _cc_internal.combine_cc_toolchain_variables(cc_toolchain._build_variables, common_vars)

def _setup_common_compile_build_variables_internal(
        *,
        feature_configuration,
        fdo_build_stamp,  # str
        is_using_memprof,  # bool
        includes = [],  # [str]
        variables_extension = [],  # [dict{str,object}]
        additional_build_variables = {},  # dict{str,str}
        include_dirs = depset(),
        quote_include_dirs = depset(),
        system_include_dirs = depset(),
        framework_include_dirs = depset(),
        defines = depset(),
        local_defines = depset(),
        external_include_dirs = depset()):
    result = {}

    if feature_configuration.is_enabled("use_header_modules"):
        result[_VARS.MODULE_FILES] = []
    result[_VARS.INCLUDE_PATHS] = include_dirs
    result[_VARS.QUOTE_INCLUDE_PATHS] = quote_include_dirs
    result[_VARS.SYSTEM_INCLUDE_PATHS] = system_include_dirs
    if includes:
        result[_VARS.INCLUDES] = _cc_internal.intern_string_sequence_variable_value(includes)
    result[_VARS.FRAMEWORK_PATHS] = framework_include_dirs

    if is_using_memprof:
        result[_VARS.IS_USING_MEMPROF] = "1"

    # Stamp FDO builds with FDO subtype string
    all_defines = defines.to_list() + local_defines.to_list() + (
        ["BUILD_FDO_TYPE=\"" + fdo_build_stamp + "\""] if fdo_build_stamp else []
    )
    result[_VARS.PREPROCESSOR_DEFINES] = _cc_internal.intern_string_sequence_variable_value(all_defines)
    result = result | additional_build_variables

    for key, value in variables_extension.items():
        if type(value) == type([]):
            result[key] = _cc_internal.intern_string_sequence_variable_value(value)
        elif type(value) == type(""):
            result[key] = value
        elif type(value) == type(depset()):
            for e in value.to_list():
                if type(e) != type(""):
                    fail("for string_sequence_variables_extension, got element of type " + type(e) + ", want string")
            result[key] = value
        else:
            fail("for variable extension key:" + key + ", got element of type " + type(value) + ", want string")

    if external_include_dirs:
        result[_VARS.EXTERNAL_INCLUDE_PATHS] = external_include_dirs
    return _cc_internal.cc_toolchain_variables(vars = result)

def _get_fdo_build_stamp(cpp_configuration, fdo_context, feature_configuration):
    branch_fdo_profile = getattr(fdo_context, "branch_fdo_profile", None)
    if branch_fdo_profile:
        branch_fdo_mode = branch_fdo_profile.branch_fdo_mode
        if branch_fdo_mode == "auto_fdo":
            return "AFDO" if feature_configuration.is_enabled("autofdo") else None
        if branch_fdo_mode == "xbinary_fdo":
            return "XFDO" if feature_configuration.is_enabled("xbinaryfdo") else None
        if branch_fdo_mode == "llvm_cs_fdo" or cpp_configuration.cs_fdo_instrument():
            return "CSFDO"
    if branch_fdo_profile or cpp_configuration.fdo_instrument():
        return "FDO"
    return None

# Note: this method is side-effect free, callers should add fdo inputs to
# cc_compile_action_builder themselves
def get_specific_compile_build_variables(
        feature_configuration,
        use_pic,
        source_file = None,
        output_file = None,
        dotd_file = None,
        diagnostics_file = None,
        gcno_file = None,
        dwo_file = None,
        lto_indexing_file = None,
        thinlto_index = None,
        thinlto_bitcode_file = None,
        thinlto_output_object_file = None,
        using_fission = False,
        code_coverage_enabled = False,
        cpp_module_map = None,
        direct_module_maps = depset(),
        user_compile_flags = [],
        additional_build_variables = {},
        fdo_build_variables = {}):
    """Creates a CcToolchainVariables instance

    Args:
        feature_configuration: (FeatureConfiguration)
        use_pic: (bool)
        source_file: (File)
        output_file: (File)
        dotd_file: (File)
        diagnostics_file: (File)
        gcno_file: (File)
        dwo_file: (File)
        lto_indexing_file: (File)
        thinlto_index: (File)
        thinlto_bitcode_file: (File)
        thinlto_output_object_file: (File)
        using_fission: (bool)
        code_coverage_enabled: (bool)
        cpp_module_map: (File)
        direct_module_maps: (depset[File])
        user_compile_flags: (list[str])
        additional_build_variables: (dict{str,str})
        fdo_build_variables: (dict{str,str})

    Returns:
        (Variables)
    """
    result = {}

    if feature_configuration.is_enabled("module_maps") and cpp_module_map:
        result[_VARS.MODULE_NAME] = cpp_module_map.name
        result[_VARS.MODULE_MAP_FILE] = cpp_module_map.file
        result[_VARS.DEPENDENT_MODULE_MAP_FILES] = direct_module_maps

    result[_VARS.USER_COMPILE_FLAGS] = _cc_internal.intern_string_sequence_variable_value(user_compile_flags)
    if source_file:
        result[_VARS.SOURCE_FILE] = source_file
    if output_file:
        result[_VARS.OUTPUT_FILE] = output_file

    # Set dependency_file to enable <object>.d file generation.
    if dotd_file:
        result[_VARS.DEPENDENCY_FILE] = dotd_file

    # Set diagnostics_file to enable <object>.dia file generation.
    if diagnostics_file:
        result[_VARS.SERIALIZED_DIAGNOSTICS_FILE] = diagnostics_file

    if gcno_file:
        result[_VARS.GCOV_GCNO_FILE] = gcno_file
    elif code_coverage_enabled:
        # TODO: Blaze currently uses `gcov_gcno_file` to detect if code coverage
        # is enabled. It should use a different signal.
        result[_VARS.GCOV_GCNO_FILE] = ""

    if dwo_file:
        result[_VARS.PER_OBJECT_DEBUG_INFO_FILE] = dwo_file
    if using_fission:
        result[_VARS.IS_USING_FISSION] = ""
    if lto_indexing_file:
        result[_VARS.LTO_INDEXING_BITCODE_FILE] = lto_indexing_file
    if thinlto_index:
        result[_VARS.THINLTO_INDEX] = thinlto_index
    if thinlto_bitcode_file:
        result[_VARS.THINLTO_INPUT_BITCODE_FILE] = thinlto_bitcode_file
    if thinlto_output_object_file:
        result[_VARS.THINLTO_OUTPUT_OBJECT_FILE] = thinlto_output_object_file
    if use_pic:
        result[_VARS.PIC] = ""
    result = result | additional_build_variables
    result = result | fdo_build_variables
    return _cc_internal.cc_toolchain_variables(vars = result)

_SOURCE_TYPES_FOR_CXXOPTS = set(
    extensions.CC_SOURCE +
    extensions.CC_HEADER +
    extensions.CLIF_INPUT_PROTO +
    extensions.CPP_MODULE_MAP +
    extensions.OBJCPP_SOURCE,
)

# buildifier: disable=function-docstring
def get_copts(
        language,
        cpp_configuration,
        source_file,
        conlyopts,
        copts,
        cxxopts,
        label,
        override_extension = None):
    if override_extension != None:
        extension = override_extension
    elif source_file.extension:
        extension = "." + source_file.extension
    else:
        extension = ""
    result = []
    result.extend(_copts_from_options(language, cpp_configuration, extension))
    result.extend(copts)
    if extension in extensions.C_SOURCE:
        result.extend(conlyopts)
    if extension in _SOURCE_TYPES_FOR_CXXOPTS:
        result.extend(cxxopts)
    if label:
        result.extend(_cc_internal.per_file_copts(cpp_configuration, source_file, label))
    return result

def _copts_from_options(language, cpp_configuration, extension):
    result = []
    result.extend(cpp_configuration.copts)
    if extension in extensions.C_SOURCE:
        result.extend(cpp_configuration.conlyopts)
    if extension in _SOURCE_TYPES_FOR_CXXOPTS:
        result.extend(cpp_configuration.cxxopts)
    if extension in extensions.OBJC_SOURCE or extension in extensions.OBJCPP_SOURCE or (
        language == "objc" and extension in extensions.CC_HEADER
    ):
        result.extend(cpp_configuration.objccopts)
    return result

def get_fdo_variables_and_inputs(
        cc_toolchain,
        fdo_context,
        feature_configuration,
        cpp_configuration):
    auxiliary_fdo_inputs_list = _get_auxiliary_fdo_inputs_list(
        cc_toolchain = cc_toolchain,
        fdo_context = fdo_context,
        feature_configuration = feature_configuration,
    )
    fdo_build_variables = _setup_fdo_build_variables(
        cc_toolchain = cc_toolchain,
        fdo_context = fdo_context,
        auxiliary_fdo_inputs_list = auxiliary_fdo_inputs_list,
        feature_configuration = feature_configuration,
        fdo_instrument = cpp_configuration.fdo_instrument(),
        cs_fdo_instrument = cpp_configuration.cs_fdo_instrument(),
    )

    # TODO(b/396122076): once starlarkification of CcStaticCompilationHelper is done, check whether
    # we can change auxiliary_fdo_inputs from depset to list.
    auxiliary_fdo_inputs = depset(direct = auxiliary_fdo_inputs_list)
    return fdo_build_variables, auxiliary_fdo_inputs

def _setup_fdo_build_variables(
        cc_toolchain,
        fdo_context,
        auxiliary_fdo_inputs_list,
        feature_configuration,
        fdo_instrument,
        cs_fdo_instrument):
    """Populates FDO build variables."""
    variables = {}
    if feature_configuration.is_enabled("fdo_instrument"):
        variables["fdo_instrument_path"] = fdo_instrument
    if feature_configuration.is_enabled("cs_fdo_instrument"):
        variables["cs_fdo_instrument_path"] = cs_fdo_instrument

    if not (getattr(fdo_context, "branch_fdo_profile", None) or
            getattr(fdo_context, "prefetch_hints_artifact", None) or
            getattr(fdo_context, "propeller_optimize_info", None) or
            getattr(fdo_context, "memprof_profile_artifact", None)):
        return variables

    prefetch_hints_artifact = getattr(fdo_context, "prefetch_hints_artifact", None)
    if prefetch_hints_artifact:
        variables["fdo_prefetch_hints_path"] = prefetch_hints_artifact.path

    if _should_pass_propeller_profiles(cc_toolchain, fdo_context, feature_configuration):
        # _should_pass_propeller_profiles() ensures that fdo_context.propeller_optimize_info
        # is not None.
        cc_artifact = getattr(fdo_context.propeller_optimize_info, "cc_profile", None)
        if cc_artifact:
            variables["propeller_optimize_cc_path"] = cc_artifact.path
        ld_artifact = getattr(fdo_context.propeller_optimize_info, "ld_profile", None)
        if ld_artifact:
            variables["propeller_optimize_ld_path"] = ld_artifact.path

    memprof_profile_artifact = getattr(fdo_context, "memprof_profile_artifact", None)
    if memprof_profile_artifact:
        variables["memprof_profile_path"] = memprof_profile_artifact.path

    branch_fdo_profile = getattr(fdo_context, "branch_fdo_profile", None)
    if (branch_fdo_profile and
        auxiliary_fdo_inputs_list and
        (
            feature_configuration.is_enabled("autofdo") or
            feature_configuration.is_enabled("xbinaryfdo") or
            (
                feature_configuration.is_enabled("fdo_optimize") and
                branch_fdo_profile.branch_fdo_mode in ("llvm_fdo", "llvm_cs_fdo")
            )
        )):
        variables["fdo_profile_path"] = branch_fdo_profile.profile_artifact.path
    return variables

def _get_auxiliary_fdo_inputs_list(
        cc_toolchain,
        fdo_context,
        feature_configuration):
    """Returns the auxiliary files that need to be added to CppCompileAction."""
    auxiliary_inputs = []

    prefetch_hints_artifact = getattr(fdo_context, "prefetch_hints_artifact", None)
    if prefetch_hints_artifact:
        auxiliary_inputs.append(prefetch_hints_artifact)

    if _should_pass_propeller_profiles(cc_toolchain, fdo_context, feature_configuration):
        # _should_pass_propeller_profiles() ensures that fdo_context.propeller_optimize_info
        # is not None.
        cc_artifact = getattr(fdo_context.propeller_optimize_info, "cc_profile", None)
        if cc_artifact:
            auxiliary_inputs.append(cc_artifact)
        ld_artifact = getattr(fdo_context.propeller_optimize_info, "ld_profile", None)
        if ld_artifact:
            auxiliary_inputs.append(ld_artifact)

    memprof_profile_artifact = getattr(fdo_context, "memprof_profile_artifact", None)
    if memprof_profile_artifact:
        auxiliary_inputs.append(memprof_profile_artifact)

    branch_fdo_profile = getattr(fdo_context, "branch_fdo_profile", None)

    # If --fdo_optimize was not specified, we don't have any additional inputs.
    if branch_fdo_profile:
        auxiliary_inputs.append(branch_fdo_profile.profile_artifact)

    return auxiliary_inputs

def _should_pass_propeller_profiles(
        cc_toolchain,
        fdo_context,
        feature_configuration):
    """Returns whether Propeller profiles should be passed to a compile action."""
    if cc_toolchain._is_tool_configuration:
        # Propeller doesn't make much sense for host builds.
        return False

    if getattr(fdo_context, "propeller_optimize_info", None) == None:
        # No Propeller profiles to pass.
        return False

    # Don't pass Propeller input files if they have no effect (i.e. for ThinLTO).
    return (not feature_configuration.is_enabled("thin_lto") or
            feature_configuration.is_enabled("propeller_optimize_thinlto_compile_actions"))

def get_linkstamp_compile_variables(
        source_file,
        output_file,
        label_replacement,
        output_replacement,
        additional_linkstamp_defines,
        build_info_header_artifacts,
        feature_configuration,
        cc_toolchain,
        needs_pic):
    """Returns variables for linkstamp compilation.

    Args:
        source_file: The linkstamp source file to be compiled.
        output_file: The output object file.
        label_replacement: String to replace ${LABEL} in linkstamp defines.
        output_replacement: String to replace ${OUTPUT_PATH} in linkstamp defines.
        additional_linkstamp_defines: A list of additional defines for linkstamp compilation.
        build_info_header_artifacts: A list of build info header artifacts.
        feature_configuration: The feature configuration.
        cc_toolchain: The C++ toolchain provider.
        needs_pic: Whether PIC compilation is needed.

    Returns:
        CcToolchainVariables for linkstamp compilation.
    """

    if not _cc_common_internal.action_is_enabled(
        feature_configuration = feature_configuration,
        action_name = "linkstamp-compile",
    ):
        fail("Action 'linkstamp-compile' is not configured.")
    fdo_build_stamp = _get_fdo_build_stamp(
        cc_toolchain._cpp_configuration,
        cc_toolchain._fdo_context,
        feature_configuration,
    )
    code_coverage_enabled = feature_configuration.is_enabled("coverage")
    copts = get_copts(
        language = "c++",  # The only language that receives special treatment is "objc".
        cpp_configuration = cc_toolchain._cpp_configuration,
        source_file = source_file,
        conlyopts = [],
        copts = [],
        cxxopts = [],
        label = None,
    )
    defines = _compute_all_linkstamp_defines(
        label_replacement = label_replacement,
        output_replacement = output_replacement,
        additional_linkstamp_defines = additional_linkstamp_defines,
        cc_toolchain = cc_toolchain,
        fdo_build_stamp = fdo_build_stamp,
        code_coverage_enabled = code_coverage_enabled,
    )
    return create_compile_variables(
        feature_configuration = feature_configuration,
        cc_toolchain = cc_toolchain,
        source_file = source_file,
        output_file = output_file,
        use_pic = needs_pic,
        preprocessor_defines = depset(defines),
        includes = [a.path for a in build_info_header_artifacts],
        include_directories = depset(["."]),
        user_compile_flags = copts,
    )

def _compute_all_linkstamp_defines(
        label_replacement,
        output_replacement,
        additional_linkstamp_defines,
        cc_toolchain,
        fdo_build_stamp,
        code_coverage_enabled):
    """Computes defines for linkstamp compilation."""
    defines = [
        'GPLATFORM="' + cc_toolchain.toolchain_id + '"',
        "BUILD_COVERAGE_ENABLED=" + ("1" if code_coverage_enabled else "0"),
        # G3_TARGET_NAME is a C string literal that normally contain the label of the target
        # being linked.  However, they are set differently when using shared native deps. In
        # that case, a single .so file is shared by multiple targets, and its contents cannot
        # depend on which target(s) were specified on the command line.  So in that case we
        # have to use the (obscure) name of the .so file instead, or more precisely the path of
        # the .so file relative to the workspace root.
        'G3_TARGET_NAME="${LABEL}"',
        # G3_BUILD_TARGET is a C string literal containing the output of this
        # link.  (An undocumented and untested invariant is that G3_BUILD_TARGET is the
        # location of the executable, either absolutely, or relative to the directory part of
        # BUILD_INFO.)
        'G3_BUILD_TARGET="${OUTPUT_PATH}"',
    ]
    if additional_linkstamp_defines:
        defines.extend(additional_linkstamp_defines)

    if fdo_build_stamp:
        defines.append('BUILD_FDO_TYPE="' + fdo_build_stamp + '"')

    return [
        define.replace("${LABEL}", label_replacement).replace("${OUTPUT_PATH}", output_replacement)
        for define in defines
    ]

# LINT.ThenChange(@rules_cc//cc/private/compile/compile_build_variables.bzl:forked_exports)
